﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace etcetera.Mvc.UI.Html
{
    public class DropDownListEx<T>
    {
        private readonly string _name;
        private readonly OptionGroupSelectList<T> _optionGroupSelectList;
        private readonly IDictionary<string, object> _attributes;
        private readonly string _optionLabel;

        /// <summary>
        /// Initializes a new instance of the <see cref="DropDownListEx"/> class.
        /// </summary>
        /// <param name="name">The name of the form field.
        /// <param name="optionGroups">The option groups.</param>
        /// <param name="htmlAttributes">An object containing the HTML attributes for the element.</param>
        public DropDownListEx(string name, OptionGroupSelectList<T> optionGroupSelectList, IDictionary<string, object> htmlAttributes)
        {
            _name = name;
            _optionGroupSelectList = optionGroupSelectList;
            _attributes = htmlAttributes;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DropDownListEx&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="name">The name of the form field.
        /// <param name="optionGroupSelectList">The option group list.</param>
        /// <param name="optionLabel">Provides the text for a default empty valued option, if it is not null.</param>
        public DropDownListEx(string name, OptionGroupSelectList<T> optionGroupSelectList, string optionLabel)
        {
            _name = name;
            _optionGroupSelectList = optionGroupSelectList;
            _optionLabel = optionLabel;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DropDownListEx"/> class.
        /// </summary>
        /// <param name="name">The name of the form field.
        /// <param name="optionGroupSelectList">The option groups.</param>
        /// /// <param name="optionLabel">Provides the text for a default empty valued option, if it is not null.</param>
        /// <param name="htmlAttributes">An object containing the HTML attributes for the element.</param>
        public DropDownListEx(string name, OptionGroupSelectList<T> optionGroupSelectList, string optionLabel, IDictionary<string, object> htmlAttributes)
        {
            _name = name;
            _optionGroupSelectList = optionGroupSelectList;
            _optionLabel = optionLabel;
            _attributes = htmlAttributes;
        }

        /// <summary>
        /// Converts the specified attributes dictionary of key-value pairs into a string of HTML attributes. 
        /// </summary>
        /// <returns></returns>
        private static string BuildHtmlAttributes(IDictionary<string, object> attributes)
        {
            if (attributes == null || attributes.Count == 0)
            {
                return string.Empty;
            }

            const string attributeFormat = "{0}=\"{1}\"";

            return string.Join(" ", attributes.Select(pair => string.Format(attributeFormat, pair.Key, pair.Value)).ToArray());
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            string attrs = BuildHtmlAttributes(_attributes);
            if (attrs.Length > 0)
                sb.AppendFormat("<select id=\"{0}\" name=\"{0}\" {1}>", _name, attrs);
            else
                sb.AppendFormat("<select id=\"{0}\" name=\"{0}\">", _name);

            if (!string.IsNullOrEmpty(_optionLabel))
                sb.AppendFormat("<option value=\"\">{0}</option>", _optionLabel);

            int i = 0;
            string oldOptGroupLabel = string.Empty;

            foreach (var item in _optionGroupSelectList.Items)
            {
                try
                {
                    string optGroupLabel = _optionGroupSelectList.OptionGroupPropertyValueFunc(item);
                    if (!oldOptGroupLabel.Equals(optGroupLabel, StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (i > 0)
                            sb.Append("</optgroup>");
                        sb.AppendFormat("<optgroup label=\"{0}\">", optGroupLabel);

                        oldOptGroupLabel = optGroupLabel;
                    }

                }
                catch { }

                SelectListItem selectListItem = _optionGroupSelectList.SelectList.ElementAt(i++);
                sb.AppendFormat("<option value=\"{0}\"{2}>{1}</option>",
                                selectListItem.Value,
                                selectListItem.Text,
                                selectListItem.Selected ? " selected" : string.Empty);
            }
            
            sb.Append("</optgroup>");
            sb.Append("</select>");

            return sb.ToString();
        }
    }
}
